//
//  FilePack.m
//  pa
//
//  Created by HaiJiao Chen on 12-7-21.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#define _LZMA_UINT32_IS_ULONG
#include "7zFile.h"
#include "Alloc.h"
#include "LzmaDec.h"
#include "LzmaEnc.h"
#import "FilePack.h"


NSString *const MultiLanguageChangeNotification = @"MultiLanguageChangeNotification";

NSMutableDictionary* __mute_dic = nil;
NSString* __current_lan = nil;


static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
static void SzFree(void *p, void *address) { p = p; MyFree(address); }
static ISzAlloc g_Alloc = { SzAlloc, SzFree };


#define IN_BUF_SIZE (1 << 16)
#define OUT_BUF_SIZE (1 << 16)

static SRes Decode2(CLzmaDec *state, ISeqOutStream *outStream, ISeqInStream *inStream,
                    UInt64 unpackSize)
{
    int thereIsSize = (unpackSize != (UInt64)(Int64)-1);
    Byte inBuf[IN_BUF_SIZE];
    Byte outBuf[OUT_BUF_SIZE];
    size_t inPos = 0, inSize = 0, outPos = 0;
    LzmaDec_Init(state);
    for (;;)
    {
        if (inPos == inSize)
        {
            inSize = IN_BUF_SIZE;
            RINOK(inStream->Read(inStream, inBuf, &inSize));
            inPos = 0;
        }
        {
            SRes res;
            SizeT inProcessed = inSize - inPos;
            SizeT outProcessed = OUT_BUF_SIZE - outPos;
            ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
            ELzmaStatus status;
            if (thereIsSize && outProcessed > unpackSize)
            {
                outProcessed = (SizeT)unpackSize;
                finishMode = LZMA_FINISH_END;
            }
            
            res = LzmaDec_DecodeToBuf(state, outBuf + outPos, &outProcessed,
                                      inBuf + inPos, &inProcessed, finishMode, &status);
            inPos += inProcessed;
            outPos += outProcessed;
            unpackSize -= outProcessed;
            
            if (outStream)
                if (outStream->Write(outStream, outBuf, outPos) != outPos)
                    return SZ_ERROR_WRITE;
            
            outPos = 0;
            
            if (res != SZ_OK || (thereIsSize && unpackSize == 0))
                return res;
            
            if (inProcessed == 0 && outProcessed == 0)
            {
                if (thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK)
                    return SZ_ERROR_DATA;
                return res;
            }
        }
    }
}

static SRes ReadStream(ISeqOutStream *outStream, ISeqInStream *inStream)
{
    UInt64 unpackSize;
    int i;
    SRes res = 0;
    
    CLzmaDec state;
    
    /* header: 5 bytes of LZMA properties and 8 bytes of uncompressed size */
    unsigned char header[LZMA_PROPS_SIZE + 8];
    
    /* Read and parse header */
    
    RINOK(SeqInStream_Read(inStream, header, sizeof(header)));
    
    unpackSize = 0;
    for (i = 0; i < 8; i++)
        unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8);
    
    LzmaDec_Construct(&state);
    RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc));
    res = Decode2(&state, outStream, inStream, unpackSize);
    LzmaDec_Free(&state, &g_Alloc);
    return res;
}

typedef struct
{
    ISeqOutStream s;
    NSMutableData* buffer;
} CBUFFERStream;

typedef struct
{
    ISeqInStream s;
    unsigned int pos;
    const void* mem;
    unsigned int mem_size;
} CMEMInStream;

WRes mem_Read(CMEMInStream *p, void *buf, size_t *size)
{
    size_t originalSize = *size;
    if (originalSize == 0)
        return 0;
    
    if (p->mem_size-p->pos <= 0)
    {
        return 1;
    }
    if (originalSize > p->mem_size-p->pos)
    {
        memcpy(buf, p->mem+p->pos, p->mem_size-p->pos);
        p->pos = p->mem_size;
        return 0;//fail
    }
    else
    {
        memcpy(buf, p->mem+p->pos, originalSize);
        p->pos += originalSize;
        return 0;//successfull
    }
}


size_t buffer_Write(CBUFFERStream *p, const void *buf, size_t size)
{
    [p->buffer appendBytes:buf length:size];
    return size;
}


void lzma_decompress(NSData* in, NSMutableData* out)
{    
    if (in != nil && out != nil)
    {
    }
}

@implementation FilePack
- (id)initWithFile:(NSString*)file
{
    self = [super init];
    if (self)
    {
        NSBundle* bundle = [NSBundle mainBundle];
        NSString* path = [bundle resourcePath];
        NSData* data = [[NSData alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", path, file]];
        

        CMEMInStream inStream;
        CBUFFERStream outStream;
        //in res;
        inStream.pos = 0;
        inStream.mem = [data bytes];
        inStream.mem_size = [data length];
        inStream.s.Read = (SRes (*)(void *p, void *buf, size_t *size))mem_Read;
        
        //out res
        outStream.buffer = [[NSMutableData alloc] initWithCapacity:0];
        outStream.s.Write = (size_t (*)(void *p, const void *buf, size_t size))buffer_Write;
        ReadStream(&outStream.s, &inStream.s);
        [data release];

        
        const char* bytes = [outStream.buffer bytes];
        memcpy(&numOfString, bytes, sizeof(int));
        
        texts = [[NSMutableArray alloc] initWithCapacity:numOfString];
        const char** strs = (const char**)(bytes+4);
        for (int i = 0; i < numOfString; i++)
        {
            const char* str = strs[i]+ ((int)bytes);
            [texts addObject:[NSString stringWithUTF8String:str]];
        }
        
        [outStream.buffer release];
    }
    return self;
}

- (NSString*)string:(int)text
{
    if (text >= 0 && text < numOfString)
    {
        return [texts objectAtIndex:text];
    }
    return nil;
}

- (int)numOfString
{
    return numOfString;
}

+ (void)setCurrentlanguage:(NSString*)lan
{
    if (__mute_dic == nil) __mute_dic = [[NSMutableDictionary alloc] initWithCapacity:10];
    FilePack* pack = [__mute_dic objectForKey:lan];
    if (pack == nil)
    {
        pack = [[FilePack alloc] initWithFile:[NSString stringWithFormat:@"c_%@.bin", lan]];
        [__mute_dic setObject:pack forKey:lan];
        [pack release];
    }
    [__current_lan release];
    __current_lan = [lan copy];
    [[NSNotificationCenter defaultCenter] postNotificationName:MultiLanguageChangeNotification object:nil];
}

+ (NSString*)currentlanguage
{
    return __current_lan;
}

+ (NSString*)getStringByID:(int)text
{
    FilePack* pack = [__mute_dic objectForKey:__current_lan];
    return [pack string:text];
}

@end
